iT邦幫忙

2026 iThome 鐵人賽

DAY 22
0
Modern Web

WebMCP:30 天打造 AI Agent 看得懂、也操作得動的網站系列 第 22 篇

Day 22|Iframe 裡的 Tool 誰說了算?跨 Origin WebMCP 權限實驗

  • 分享至 

  • xImage
  •  

本篇重點

Day 21 用 Session 與後端權限檢查,決定使用者能操作哪些資料。今天把頁面拆成父頁面與 iframe,測試另一個問題:父頁面怎麼取得不同 Origin 的工具?

本篇用三個設定逐一對照:

  1. 父頁面的 allow="tools":允許 iframe 使用工具能力。
  2. 子頁面的 exposedTo:指定哪些 Origin 可以取得並執行這個工具。
  3. 父頁面的 fromOrigins:指定這次要向哪些 Origin 查詢工具。

頁面嵌入成功,不等於工具已開放給父頁面。

開啟兩個不同 Origin 的頁面

在專案資料夾的終端機執行:

python day22_server.py

保持終端機開啟,再用已啟用 WebMCP 的 Chrome 開啟:

http://localhost:8090/

這個指令會同時啟動兩個本地服務:

頁面 Origin 工作
Parent/父頁面 http://localhost:8090 嵌入客服元件、查詢與執行工具
Child/客服 iframe http://localhost:8091 註冊 get_widget_status

Origin 包含協定、主機名稱與連接埠。這兩個網址雖然都使用 localhost,連接埠不同,仍屬於不同 Origin。操作時統一使用上面的 localhost 網址。

本篇使用網頁上的「取得工具清單」與「執行 get_widget_status」按鈕操作,核對左側「父頁面取得的 Tools」與「執行結果」。

開啟頁面後,先選「A|完整授權」,確認父頁面顯示「WebMCP 查詢 API 已就緒」,子頁面顯示「已註冊 get_widget_status」。後面依序測試 B、C、D,每次切換後都重新按「取得工具清單」。

圖片 1|父頁面與跨 Origin 客服元件
https://ithelp.ithome.com.tw/upload/images/20261001/20121296twP2JICmfg.png

三個設定各放在哪裡?

父頁面:委派 tools 能力

父頁面嵌入客服元件時,加上 allow="tools":

<iframe
  src="http://localhost:8091/day22-child.html?mode=complete&generation=1"
  allow="tools"
></iframe>

範例伺服器也在父頁面的回應標頭列出可委派的來源:

Permissions-Policy: tools=(self "http://localhost:8091")

HTTP 標頭與 iframe 的設定要一起核對。若上層標頭禁止某個來源,單靠 iframe 的 allow 無法放寬它。

子頁面:用 exposedTo 指定父 Origin

客服元件只提供讀取狀態的工具,回傳固定的上線狀態與排隊人數:

await document.modelContext.registerTool({
  name: 'get_widget_status',
  description: 'Read the demo support widget status and queue length.',
  inputSchema: {
    type: 'object',
    properties: {},
    additionalProperties: false
  },
  annotations: { readOnlyHint: true },
  execute: async () => JSON.stringify({
    status: 'success',
    online: true,
    queue: 3,
    simulated: true
  })
}, {
  exposedTo: ['http://localhost:8090']
});

exposedTo 放在 registerTool 的第二個參數,填的是獲准使用工具的父 Origin。

父頁面:用 fromOrigins 指定子 Origin

const tools = await document.modelContext.getTools({
  fromOrigins: ['http://localhost:8091']
});

fromOrigins 填的是工具所在的子 Origin。兩個設定方向不同:子頁面允許父頁面使用,父頁面再指定要查詢子頁面。

範例使用本地 localhost 開發環境;部署到網站時,將兩邊改成實際的 HTTPS Origin,包含正確的連接埠,不填路徑。

父頁面:執行取得的工具

本系列使用的 Chrome 153,在 executeTool 傳入序列化的 JSON:

const tool = tools.find(
  item => item.name === 'get_widget_status'
    && item.origin === 'http://localhost:8091'
);
if (tool) {
  const result = await document.modelContext.executeTool(tool, '{}');
  console.log(result);
}

範例程式保留 getTools 回傳的工具物件,並核對所屬 iframe;Chrome 155 以上則傳入 JavaScript 物件 {}。

先跑一次完整授權

  1. 「測試情境」選擇「A|完整授權」。
  2. 等子頁面顯示「已註冊 get_widget_status」。
  3. 往下找到「父頁面取得的 Tools」,按「取得工具清單」。
  4. 清單會列出 get_widget_status,來源是 http://localhost:8091。
  5. 按「執行 get_widget_status」,核對以下結果:
{
  "status": "success",
  "online": true,
  "queue": 3,
  "simulated": true
}

子頁面也會顯示同一份結果。工具在子頁面執行,由父頁面接收回傳值。

圖片 2|完整授權後,父頁面取得並執行工具
https://ithelp.ithome.com.tw/upload/images/20261001/20121296jD5d3cWK85.png

移除 iframe 的 allow

  1. 回到上方「測試情境」,選擇「B|移除 allow="tools"」。
  2. 確認設定顯示「allow:未設定」,iframe 程式碼也沒有 allow 屬性。
  3. 往下查看客服元件,會顯示註冊失敗與 NotAllowedError,錯誤訊息指出受到 Permissions Policy 限制。
  4. 再往下按「取得工具清單」,父頁面的結果為 [],執行按鈕維持停用。

客服頁面仍然正常顯示,但工具註冊已被 Permissions Policy 擋下。

圖片 3-1|移除 iframe 的 allow 設定
https://ithelp.ithome.com.tw/upload/images/20261001/20121296UvTu8ErtoG.png

圖片 3-2|工具註冊被阻擋,父頁面取得空清單
https://ithelp.ithome.com.tw/upload/images/20261001/201212967jSMOUTM7w.png

移除子頁面的 exposedTo

  1. 回到上方,選擇「C|移除 exposedTo」。
  2. 確認 allow 保留 tools,exposedTo 顯示「未設定」。
  3. 往下查看客服元件,這次會顯示「已註冊 get_widget_status」。
  4. 按「取得工具清單」,父頁面的結果仍為 []。

子頁面可以註冊自己的工具;要讓不同 Origin 的父頁面取得工具,還需要透過 exposedTo 開放。

圖片 4|子頁面已註冊,但沒有 exposedTo
https://ithelp.ithome.com.tw/upload/images/20261001/20121296xSSaCzdYJM.png

最後移除 fromOrigins

  1. 回到上方,選擇「D|不指定 fromOrigins」。
  2. 確認 allow 與 exposedTo 都已設定,fromOrigins 顯示「未設定」,查詢程式改成 getTools()。
  3. 往下確認客服元件已註冊,再按「取得工具清單」。
  4. 父頁面的結果為 [],執行按鈕維持停用。

本例父頁面本身沒有註冊工具,因此預設的同 Origin 查詢會得到空清單。指定子 Origin 的 fromOrigins 後,才能將符合授權條件的跨 Origin 工具納入查詢。

圖片 5-1|父頁面改用未指定來源的 getTools()
https://ithelp.ithome.com.tw/upload/images/20261001/201212969qHtJYabRO.png

圖片 5-2|子頁面已註冊,父頁面的預設查詢仍是空清單
https://ithelp.ithome.com.tw/upload/images/20261001/201212968pfYiu0Rz4.png

保存本次測試紀錄

完成 A~D 後,按頁面下方的「下載紀錄 JSON」。檔案會保留各情境的註冊狀態、查詢選項、工具清單與執行結果。

切換情境會清除畫面上的舊工具清單與執行結果,本頁紀錄則繼續累積。重新整理會清空紀錄,因此先下載,再重新整理或關閉頁面。

四種情境的實測結果

以下是本地頁面按鈕直接呼叫 WebMCP API 的結果:

情境 allow exposedTo fromOrigins 子頁面註冊 父頁面結果
A 完整授權 有 有 有 成功 取得 1 個工具,執行回傳 queue: 3
B 移除 allow 無 有 有 NotAllowedError 空清單
C 移除 exposedTo 有 無 有 成功 空清單
D 移除 fromOrigins 有 有 無 成功 空清單

這四輪分別呈現註冊、跨 Origin 開放與查詢來源三個環節。範例的 postMessage 只回報子頁面的載入與註冊狀態;取得清單及執行工具都透過 WebMCP。

為什麼要這麼麻煩?

因為 iframe 常用來嵌:

付款
客服
登入
地圖
表單
第三方 SaaS Widget

如果任何嵌入頁面都能:

看到 Parent 的 Tools
或把自己的高權限 Tools 自動塞給 Parent Agent

攻擊面會非常大。

不要把付款 iframe 的 Tool 直接設計成 charge_card

就算是可信付款 Provider,我也會偏向:

get_payment_options
prepare_payment

最後的:

confirm_payment

仍然需要高風險確認與 Server Transaction Verification。

Cross-origin Trust 不是「高風險 Action 可以省略確認」的理由。

同 Origin iframe 呢?

同 Origin frame tree 的 Tool Discovery 比較直接,getTools() 預設可以看到目前 Document 有權存取的同 Origin Tools。

但 Application Design 還是要小心名稱碰撞與 Tool Lifecycle。

例如兩個 iframe 都註冊:

search

Agent 語意可能很難分辨。

名稱仍應具體:

search_help_center
search_product_catalog

實務上我什麼時候會開 exposedTo?

很少。

我會先問:

1. Parent Agent 真的需要直接操作 Child 嗎?
2. Child 能不能只暴露 read-only capability?
3. 是否含使用者資料?
4. Provider Origin 是否固定且可信?
5. 是否能用更窄的 API/Message Passing 解決?

如果只是 Widget 自己內部運作,不一定要把 Tool 提升到 Parent Agent 可見。

可帶走的重點

  1. Cross-origin iframe 的 WebMCP 是多層明確授權。
  2. Parent 用 allow="tools" 委派能力。
  3. Child Tool 用 exposedTo 指定可信 Origin。
  4. Parent getTools() 取得跨 Origin Tools 時要指定 fromOrigins。
  5. Embed 不等於 Trust,高風險操作仍要獨立確認。

參考資料


上一篇
Day 21|Agent 會操作網站後,最危險的不是幻覺:Origin、權限與最小能力
系列文
WebMCP:30 天打造 AI Agent 看得懂、也操作得動的網站 共 22 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言